package com.wn.network.interceptor

import android.util.Log
import okhttp3.*
import okio.Buffer
import okio.GzipSource
import java.net.URLDecoder
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton

/**
 * @Author: wn
 * @Date: 2024/8/2 16:54
 * @Description: 日志拦截器
 */
@Singleton
class HttpLogInterceptor @Inject constructor(/*block: (HttpLogInterceptor.() -> Unit)? = null*/) :
  Interceptor {

  private var logLevel = LogLevel.NONE // 不打印
  private var colorLevel = ColorLevel.DEBUG // 默认是debug级别的logcat
  private var logTag = TAG // 日志的logcat的Tag
  private var format: String = "yyyy-MM-dd HH:mm:ss"// 时间格式

  // 初始化 调用 HttpLogInterceptor 中方法 具体由使用者 lambda 决定调用哪个
/*
  init {
    block?.invoke(this)
  }
*/

  /**
   * 设置 logLevel
   */
  fun logLevel(level: LogLevel): HttpLogInterceptor {
    logLevel = level
    return this
  }

  /**
   * 设置 colorLevel
   */
  fun colorLevel(level: ColorLevel): HttpLogInterceptor {
    colorLevel = level
    return this
  }

  /**
   * 设置Log的Tag
   */
  fun logTag(tag: String): HttpLogInterceptor {
    logTag = tag
    return this
  }

  override fun intercept(chain: Interceptor.Chain): Response {
    // 请求
    val request = chain.request()
    return request.runCatching {
      chain.proceed(this)
    }
      .onSuccess {
        // 不打印 直接退出 intercept
        if (logLevel == LogLevel.NONE) return it
        // 设置打印所有 BODY
        // 记录请求日志
        logRequest(request, chain.connection())
        // 记录响应日志
        logResponse(it)
      }
      .onFailure {
        it.printStackTrace()
        // 打印日志  error
        logIt(it.message.toString(), ColorLevel.ERROR)
      }
      .getOrThrow()// 成功，则返回封装的值;如果失败，则抛出封装的Throwable异常
  }

  /**
   * 记录请求日志
   * @param request Request
   * @param connection Connection?
   */
  private fun logRequest(request: Request, connection: Connection?) {
    val strB = StringBuilder()
    strB.appendLine("\r\n") // 换行
    strB.appendLine("Start->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->")
    // strB 在  when 中  拼接对应输出内容
    when (logLevel) {
      LogLevel.NONE -> {}// 不打印
      LogLevel.BASIC -> logBasicReq(strB, request, connection)// 只打印行首，请求
      LogLevel.HEADERS -> logHeadersReq(strB, request, connection) // 打印请求的所有header
      LogLevel.BODY -> logBodyReq(strB, request, connection) // 打印所有
    }
    // strB 拼接 结束标识
    strB.appendLine("End-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<")
    // 打印
    logIt(strB, ColorLevel.INFO)
  }

  /**
   * 只打印行首，请求
   */
  private fun logBasicReq(strB: StringBuilder, request: Request, connection: Connection?) {
    strB.appendLine(
      "请求 method：${request.method} url:${decodeUrlStr(request.url.toString()) ?: ""} tag:" +
              "${request.tag()} protocol:${connection?.protocol() ?: Protocol.HTTP_1_1}"
    )
  }

  /**
   * 打印请求和响应的所有header
   */
  private fun logHeadersReq(strB: StringBuilder, request: Request, connection: Connection?) {
    logBasicReq(strB, request, connection)
    val headerStr: String = request.headers.joinToString("") { header ->
      "请求 Header:{${header.first} = ${header.second}}\n"
    }
    strB.appendLine(headerStr)
  }

  /**
   * 打印所有
   */
  private fun logBodyReq(strB: StringBuilder, request: Request, connection: Connection?) {
    logHeadersReq(strB, request, connection)
    strB.appendLine("请求 RequestBody:${request.body.toString()}")
  }

  /**
   * 记录响应日志
   */
  private fun logResponse(response: Response) {
    val strB = StringBuilder()
    strB.appendLine("\r\n") // 换行
    strB.appendLine("Start->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->")
    // strB 在  when 中  拼接对应输出内容
    when (logLevel) {
      LogLevel.NONE -> {}// 不打印
      LogLevel.BASIC -> logBasicRep(strB, response)// 只打印行首，响应
      LogLevel.HEADERS -> logHeadersRep(strB, response) // 打印响应的所有header
      LogLevel.BODY -> logBodyRep(strB, response) // 打印所有
    }
    strB.appendLine("End-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<-<<")
    logIt(strB, ColorLevel.INFO)
  }

  /**
   * 只打印行首，响应
   */
  private fun logBasicRep(strB: StringBuilder, response: Response) {
    strB.appendLine("响应 protocol:${response.protocol} code:${response.cacheControl} message:${response.message}")
      .appendLine("响应 request Url: ${decodeUrlStr(response.request.url.toString())}")
      .appendLine(
        "响应 发送请求事件-sentRequestTime:${toDateTimeStr(response.sentRequestAtMillis, format)} " +
                "-- " +
                "响应时间-receivedResponseTime:${
                  toDateTimeStr(response.receivedResponseAtMillis, format)
                }"
      )
      .appendLine("请求用时为: ${response.receivedResponseAtMillis - response.sentRequestAtMillis} ms")
  }

  /**
   * 打印响应的所有header
   */
  private fun logHeadersRep(strB: StringBuilder, response: Response) {
    logBasicRep(strB, response)
    val headerStr: String = response.headers.joinToString("") { header ->
      "响应 Header:{${header.first} = ${header.second}}\n"
    }
    strB.appendLine(headerStr)
  }

  /**
   * 打印所有
   */
  private fun logBodyRep(strB: StringBuilder, response: Response) {
    logHeadersRep(strB, response)
    // peek类似于clone数据流，监视，窥探，不能直接用原来的body的string流数据作为日志，会消费掉io，所有这里是peek，监测
//      val peekBody = response.peekBody(1024 * 1024)
    // 参考 HttpLoggingInterceptor实现
    val responseBody = response.body!!
    val contentLength = responseBody.contentLength()
    val source = responseBody.source()
    source.request(Long.MAX_VALUE) // Buffer the entire body.
    var buffer = source.buffer

    if ("gzip".equals(response.headers["Content-Encoding"], ignoreCase = true)) {
      GzipSource(buffer.clone()).use { gzippedResponseBody ->
        buffer = Buffer()
        buffer.writeAll(gzippedResponseBody)
      }
    }

    val contentType = responseBody.contentType()
    val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8

    // 输出的内容
    if (contentLength != 0L) {
      strB.appendLine("响应 RawData: ${buffer.clone().readString(charset)}")
    }
  }

  /**
   * 对于url编码的string解码
   */
  private fun decodeUrlStr(url: String): String? {
    return kotlin.runCatching {
      URLDecoder.decode(url, "utf-8")
    }
      .onFailure { it.printStackTrace() }
      .getOrNull()
  }

  /**
   * 打印日志
   */
  private fun logIt(any: Any, tempLevel: ColorLevel? = null) =
    when (tempLevel ?: colorLevel) {
      ColorLevel.VERBOSE -> Log.v(logTag, any.toString())
      ColorLevel.DEBUG -> Log.d(logTag, any.toString())
      ColorLevel.INFO -> Log.i(logTag, any.toString())
      ColorLevel.WARN -> Log.w(logTag, any.toString())
      ColorLevel.ERROR -> Log.e(logTag, any.toString())
    }

  companion object {
    private const val TAG = "<Retrofit>" // 默认的TAG

    // 时间格式化
    private fun toDateTimeStr(millis: Long, pattern: String): String {
      return SimpleDateFormat(pattern, Locale.getDefault()).format(millis)
    }
  }
}

/**
 * 打印日志范围
 */
enum class LogLevel {
  NONE,// 不打印
  BASIC,// 只打印行首，请求/响应
  HEADERS, // 打印请求和响应的所有header
  BODY // 打印所有
}

/**
 * Log颜色等级，应用于android Logcat分为 v、d、i、w、e
 */
enum class ColorLevel {
  VERBOSE,
  INFO,
  WARN,
  ERROR,
  DEBUG
}